#define FUSE_USE_VERSION  26

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdint.h>
/*#include <sys/epoll.h>*/

#include <fuse.h> /*contains definitions for basic functions needed to implement a filesystem*/
#include <fcntl.h> /*contains definitions of file options (the ones used with fcntl() and open() )*/
#include <errno.h> /* contains definitions of error numbers*/

#include "includes/FileSystem.h"
#include "includes/Estructuras.h"
#include "includes/ArchivoConfig.h"
#include "includes/InterfaceFAT.h"


stConfig config;
struct stConexion *ptrConexiones;
struct st_FAT_Table *tablaFAT;

/*TEMPORALES*/
static const char *hello_str = "Hello World!\n";


int main(int argc, char *argv[])
{
	int socket = 0;

	printf("************ INICIANDO FUSELAJE ***********");

	/* Leer archivo de configuracion */
	leerConfiguracion();

	/* Me conecto al RAID o PPD y hago el HANDSHAKE para pedirle la FAT despues*/
	socket = realizarConexionCliente();

	/* Armar Estructura Fat*/
	cargarEstructuraFAT(socket);

	/* Montar directorio*/
	iniciarFuse(argc, argv);

	/* Levantar Consola*/
	iniciarConsola();

	return EXIT_SUCCESS;
}

int iniciarConsola()
{
	printf("****5. Iniciando Consola **** \n");
	return EXIT_SUCCESS;
}



int levantarConfig()
{
	config.maxConexiones = 10;
	memcpy(&(config.ptoMontaje), "/fuselaje", strlen("/fuselaje"));
	memcpy(&(config.path_virtualDisk), "/home/alejandra/fat32.disk", strlen("/home/alejandra/fat32.disk"));

	printf("**** 1. Levantando Configuracion **** \n");
	printf("path virtual directory %s", config.path_virtualDisk);
	return EXIT_SUCCESS;
}

int iniciarFuse(int argc, char *argv[])
{

	printf("**** 4. Iniciando Fuse **** \n");

	printf("\n\n\n =>Por Montar directorio [%s] \n\n", config.ptoMontaje);

	/* Preparo las estructuras que utilizara FUSE */
	resultado = solicitarBootSector();
	if(!resultado) return resultado;

    fuse_main(argc, argv, &fuselage_oper, NULL);

	printf("\n\n\n =>Saliendo Fuselage !!!!\n\n");

	return EXIT_SUCCESS;
}

/************************************************************/
/******** FUNCIONES  REDEFINIDAS DE  FUSE *******************/
/************************************************************/


/* *
 * fuselage_getattr(): se ejecuta cuando me piden los atributos de un directorio/archivo
 *  devuelve 0 si fue exitoso o El numero de Error (Creo)
 *  path es el dir/archivo que quiere los stats ('/','/unArchivo','/Dir1/Archivo.txt',etc)
 *  stbuf es la estructura en la que tengo que devolver los stats
 * */
static int fuselage_getattr(const char *path, struct stat *stbuf)
{
    int res = 0;
	t_fat_file_entry file;
	t_stat temp;

	printf("\n\n\n =>Entrando a la funcion fuselage_getattr\n\n ");

    memset(stbuf, 0, sizeof(struct stat));

    if(strcmp(path, "/") == 0)
    {
        stbuf->st_mode = S_IFDIR | 0755;
        stbuf->st_nlink = 2;
    }
    else if(strcmp(path, config.ptoMontaje) == 0)
    {
        stbuf->st_mode = S_IFREG | 0444;
        stbuf->st_nlink = 1;
        stbuf->st_size = strlen(hello_str);
    }
    else
        res = -ENOENT;

    
	res = EXIT_SUCCESS;

    return res;
}


static int fuselage_read(const char *path, char *buffer, size_t size, off_t offset, struct fuse_file_info *fi)
{
	int resultado = BOOL_OK;
	
	printf("\n\n =>Entrando a la funcion fuselage_read\n\n");
	
    if(strcmp(path, config.ptoMontaje) != 0)
        return -ENOENT;
			
	resultado = obtenerArchivo(path, buffer, size, offset);
	
		
	return EXIT_SUCCESS;
}

int obtenerArchivo(const char *path, char *buffer, size_t size, off_t offset)
{
	char *retorno;
	uint32_t clusterSgte = 2; // TODO porque la data region empieza en el cluster 2, esta bien esto????
	uint32_t nroCluster, sizeCluster;
	stCluster cluster;
	st_List_FAT_Entries list_FAT_Entries;
	char*  clusterRecibido;
	int cantidadBloques, cantidadClusters = 0, i = 0;
	
	
	if(retorno = malloc(size) == NULL)  return -ENOENT; /*TODO buscar un codigo de retorno mas feliz :)*/
	sizeCluster = bootSector.bytesPerSector * bootSector.sectorPerCluster;
	
	
	/************************************************************************************************
			Primera Parte: obtengo la DirectoryEntry del archivo solicitado
	************************************************************************************************/
	
	/* 1. Necesito el cluster donde esta el Root Directory */
	if(!solicitarRootDirectory(&cluster)) return resultado;
	
	do
	{
		/* 2. de ese cluster necesito el listado de directorios */	
		if(!resultado = armarListadoDirectorios(&cluster, &list_FAT_Entries)) return resultado;
		
		/* 3. de ese listado de directorios necesito encontrar el path que me viene y en que cluster estarian sus datos */
		nroCluster = obtenerClusterDelArchivoFromList(&list_FAT_Entries, retorno, path);
		
		/* 3.1 Si no encontre el directorio en ese cluster -> pido el que sigue para buscarlo en ese otro */
		if(retorno == SIN_RESULTADOS) solicitarProximoCluster(clusterSgte + 1, &cluster);
		else break;

	} while(clusterSgte <= obtenerNroTotalDeClusters()) /* TODO esto esta bien? */
	
	
	
	/************************************************************************************************
		Segunda Parte: obtengo el primer cluster de datos del archivo
	************************************************************************************************/
	
	if(size > sizeCluster)
	{
		cantidadClusters = size / sizeCluster; /* cantidadClusters que ocupa el archivo pedido*/
		cantidadBloques = (sizeCluster / SIZE_BLOQUE);
	}	
	else
		cantidadBloques = size / SIZE_BLOQUE; /*cantidad de bloques que ocupa el archivo pedido*/
	
	
	/* nroCluster = cluster donde empiezan los DATOS propiamente dichos del archivo encontrado */
	nroBloque = getNbloqueFromNcluster(nroCluster);
	
	solicitarBloques(nroBloque, cantidadBloques, clusterRecibido);
	memcpy(buffer, clusterRecibido, cantidadBloques * SIZE_BLOQUE);
	

	if(cantidadClusters == 0)
		return EXIT_SUCCESS;
	
	
	/************************************************************************************************
		Tercera Parte: Si cantidadClusters >0 el archivo ocupa mas de un cluster 
						=> recorrer la FAT y traigo sus otros clusters 
	************************************************************************************************/
	
	for(i =0 ; i<= cantidadClusters; i++)
	{
		/* sizeCluster / SIZE_BLOQUE = es la cantidad de bloques que se leen cuando se lee UN cluster */
		cantidadBloques -= (sizeCluster / SIZE_BLOQUE);
		
		nroCluster = obtenerClusterSgteEnFAT(nroCluster);
		nroBloque = getNbloqueFromNcluster(nroCluster);
		solicitarBloques(nroBloque, cantidadBloques, clusterRecibido);
		
		offset = i * cantidadBloques * SIZE_BLOQUE;
		memcpy(&(buffer[offset]), clusterRecibido, cantidadBloques * SIZE_BLOQUE);
	}
	return EXIT_SUCCESS;
}


/* fuselage_open()
 * recibe el path del archivo que queremos abrir
 * fuse_file_info: info sobre las operaciones sobre el archivo, por ejemplo podria indicar si el file esta mapeado
 * */
static int fuselage_open(const char *path, struct fuse_file_info *fi)
{
	printf("\n\n\n =>Entrando a la funcion fuselage_open !!!!\n\n");

    if(strcmp(path, config.ptoMontaje) != 0)
        return -ENOENT; /* tomado de errno-base.h: No such file or directory */

    if((fi->flags & 3) != O_RDONLY)
        return -EACCES; /*tomado de errno.base.h: Permission denied */

    return 0;

}

static int fuselage_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi)
{
	printf("\n\n\n =>Entrando a la funcion fuselage_readdir !!!!\n\n");

    (void) offset;
    (void) fi;

    if(strcmp(path, "/") != 0)
        return -ENOENT;

    filler(buf, ".", NULL, 0);
    filler(buf, "..", NULL, 0);
    filler(buf, config.ptoMontaje + 1, NULL, 0);

    return 0;
}


